package com.imarec.web.actions;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;

import com.imarec.web.persistence.dao.ImageDAO;
import com.imarec.web.persistence.entities.ImageEntity;
import com.imarec.web.persistence.entities.ImageAttributeEntity;
import com.opensymphony.xwork2.ActionSupport;
import com.projektio.io.ImageAttribute;
import com.projektio.io.ImageTranslationService;

public class PhotoAction extends ActionSupport
{
	private static final long serialVersionUID = 1L;
	private static final String PHOTO_STORE = "D:/PHOTO_STORE/";
	private static final String MINIATURE_STORE = "D:/PHOTO_STORE/MINIATURES/";
	
	private static final ImageTranslationService translationService = new ImageTranslationService();
	
	private ImageDAO imageDAO = new ImageDAO();
	
	private ImageEntity image;
	private File photoFile;
	
	private long imageId;

	private InputStream photoDownloadStream;
	private String photoName;
	
	private InputStream miniatureDownloadStream;
	private String miniatureName;
	
	private int photoListSize;
	private List<ImageEntity> photoList;
	
	private Integer pages;
	private Integer page = 1;
	
	private Logger logger = Logger.getLogger(this.getClass());
	
	public String add()
	{
		if(photoFile == null) return "ERROR";
		
		long time = new Date().getTime();
		String outputPath = PHOTO_STORE + time + ".bmp";
		File outputFile = new File(outputPath);
		
		String miniatureOutputPath = MINIATURE_STORE + time + "-small.bmp";
		File miniatureOutputFile = new File(miniatureOutputPath);
		
		try 
		{
			BufferedInputStream is = new BufferedInputStream(new FileInputStream(photoFile));
			
			try
			{
				BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
				BufferedOutputStream mos = new BufferedOutputStream(new FileOutputStream(miniatureOutputFile));
				
				try
				{
					int content;
					while((content = is.read()) != -1)
					{
						os.write(content);
					}
					
					is.close();
					os.close();
					
					image.setFilePath(outputPath);			
					
					ImageAttribute[] attributes = translationService.calculateImageAttributes(new FileInputStream(photoFile));
					Set<ImageAttributeEntity> attributeEntities = new HashSet<ImageAttributeEntity>();
					
					for (ImageAttribute attribute : attributes)
					{
						ImageAttributeEntity attributeEntity = new ImageAttributeEntity(attribute);
						attributeEntity.setImage(image);
						
						attributeEntities.add(attributeEntity);
					}
					
					//**************************************************************************************************************************
					
					is = new BufferedInputStream(new FileInputStream(photoFile));
					Image im = (Image)ImageIO.read(is);
					is.close();
					
					int thumbWidth = 155;
			        int thumbHeight = 108;        
			        
			        //Make sure the aspect ratio is maintained, so the image is not skewed
			        double thumbRatio = (double)thumbWidth / (double)thumbHeight;
			        int imageWidth = im.getWidth(null);
			        int imageHeight = im.getHeight(null);
			        double imageRatio = (double)imageWidth / (double)imageHeight;
			        if (thumbRatio < imageRatio) {
			          thumbHeight = (int)(thumbWidth / imageRatio);
			        } else {
			          thumbWidth = (int)(thumbHeight * imageRatio);
			        }
			        
			        //Draw the scaled image
			        BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);
			        Graphics2D graphics2D = thumbImage.createGraphics();
			        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
			        graphics2D.drawImage(im, 0, 0, thumbWidth, thumbHeight, null);
			        
			        //Write the scaled image to the outputstream
			        ByteArrayOutputStream out = new ByteArrayOutputStream();
			        ImageIO.write(thumbImage,"bmp",out);
			        out.flush();
			        mos.write(out.toByteArray());
			        mos.close();
			        
			        image.setMiniaturePath(miniatureOutputPath);
					
					//**************************************************************************************************************************
					
					imageDAO.save(image, attributeEntities);
				}
				catch(IOException exc)
				{
					exc.printStackTrace();
					return ERROR;
				}
				finally
				{
					os.close();
					mos.close();
				}
			}
			catch(IOException exc)
			{
				exc.printStackTrace();
				return ERROR;
			}
			finally
			{
				is.close();
			}
		} 
		catch(IOException exc)
		{
			exc.printStackTrace();
			return ERROR;
		}
		
		return SUCCESS;
	}
	
	public String search()
	{
		int pixelTolerance = 80;
		int colorTolerance = 15;
		int imageAttributesTolerance = 75;
		
		try 
		{
			ImageAttribute[] patternAttributes = translationService.calculateImageAttributes(new FileInputStream(photoFile));
			Map<ImageAttribute, List<ImageAttributeEntity>> matchingAttributes = new HashMap<ImageAttribute, List<ImageAttributeEntity>>();
			
			for(ImageAttribute attr : patternAttributes)
			{
				matchingAttributes.put(attr, imageDAO.getMatchingAttributes(attr.getWhitePixels(), pixelTolerance, attr.getColor()[0], attr.getColor()[1], attr.getColor()[2], colorTolerance));
				
				//logger.warn(Arrays.toString(attr.getColor()));
				//matchingAttributes.put(attr, imageDAO.getMatchingAttributesForColor(attr.getColor()[0], attr.getColor()[1], attr.getColor()[2], colorTolerance));
				
			}
			

			logger.warn("Pattern attributes count: " + patternAttributes.length);
			
			Map<Long, Integer> matchedImagesCount = new HashMap<Long, Integer>();	
			List<Long> countedImages = new ArrayList<Long>();
			for(Map.Entry<ImageAttribute, List<ImageAttributeEntity>> entry : matchingAttributes.entrySet())
			{
				countedImages.clear();
				
				for(ImageAttributeEntity attribute : entry.getValue())
				{
					if(!countedImages.contains(attribute.getImage().getId()))
					{
						if(matchedImagesCount.containsKey(attribute.getImage().getId()))
						{
							matchedImagesCount.put(attribute.getImage().getId(), matchedImagesCount.get(attribute.getImage().getId()) + 1);							
						}
						else
						{
							matchedImagesCount.put(attribute.getImage().getId(), 1);
						}
						
						countedImages.add(attribute.getImage().getId());
					}
					
				}
			}
			
			logger.warn("Matching images map: " + matchedImagesCount.toString());
			
			photoList = new ArrayList<ImageEntity>();
			for(Map.Entry<Long, Integer> entry : matchedImagesCount.entrySet())
			{
				if((100.0 * entry.getValue() / patternAttributes.length) >= imageAttributesTolerance)
				{
					photoList.add(imageDAO.getImageByID(entry.getKey()));
				}
			}
			
			pages = 0;
			photoListSize = photoList.size();
		}
		catch (FileNotFoundException e) 
		{
			return ERROR;
		}
		
		return SUCCESS;
	}
	
	public String get()
	{
		ImageEntity image = imageDAO.getImageByID(imageId);
		
		if(image == null) return ERROR;
		
		try 
		{
			photoDownloadStream = new FileInputStream(new File(image.getFilePath()));
			photoName = image.getTitle() + ".bmp";
		} 
		catch (FileNotFoundException e) 
		{
			e.printStackTrace();
			return ERROR;
		}
		
		return SUCCESS;
	}
	
	public String getMiniature(){
		ImageEntity image = imageDAO.getImageByID(imageId);
		
		if(image == null) return ERROR;
		
		try 
		{
			miniatureDownloadStream = new FileInputStream(new File(image.getMiniaturePath()));
			miniatureName = image.getTitle() + ".bmp";
		} 
		catch (FileNotFoundException e) 
		{
			e.printStackTrace();
			return ERROR;
		}
		
		return SUCCESS;
	}
	
	public String list()
	{
		Integer imagesPerPage = 16;
		
		int imagesCount = imageDAO.getImagesCount();
		pages = (int)Math.ceil(1.0*imagesCount/imagesPerPage);
			
		photoList = imageDAO.list((page-1) * imagesPerPage, imagesPerPage);
		photoListSize = photoList.size();
		
		return SUCCESS;
	}

	public void setImage(ImageEntity image) 
	{
		this.image = image;
	}

	public ImageEntity getImage()
	{
		return image;
	}

	public void setPhotoFile(File photoFile)
	{
		this.photoFile = photoFile;
	}

	public File getPhotoFile()
	{
		return photoFile;
	}

	public void setPhotoDownloadStream(InputStream photoDownloadStream)
	{
		this.photoDownloadStream = photoDownloadStream;
	}

	public InputStream getPhotoDownloadStream() 
	{
		return photoDownloadStream;
	}
	
	public void setMiniatureDownloadStream(InputStream miniatureDownloadStream)
	{
		this.miniatureDownloadStream = miniatureDownloadStream;
	}

	public InputStream getMiniatureDownloadStream() 
	{
		return miniatureDownloadStream;
	}

	public void setPhotoName(String photoName)
	{
		this.photoName = photoName;
	}

	public String getPhotoName() 
	{
		return photoName;
	}
	
	public void setMiniatureName(String miniatureName)
	{
		this.miniatureName = miniatureName;
	}

	public String getMiniatureName() 
	{
		return miniatureName;
	}
	
	public long getImageId()
	{
		return imageId;
	}

	public void setImageId(long imageId)
	{
		this.imageId = imageId;
	}

	public void setPhotoList(List<ImageEntity> photoList)
	{
		this.photoList = photoList;
	}

	public List<ImageEntity> getPhotoList() 
	{
		return photoList;
	}

	public void setPhotoListSize(int photoListSize) 
	{
		this.photoListSize = photoListSize;
	}

	public int getPhotoListSize()
	{
		return photoListSize;
	}

	public void setPages(Integer pages) 
	{
		this.pages = pages;
	}

	public Integer getPages() 
	{
		return pages;
	}

	public Integer getPage() 
	{
		return page;
	}

	public void setPage(Integer page)
	{
		this.page = page;
	}
}
